from datetime import datetime
from typing import List, Optional, Dict, Any, Tuple
from sqlalchemy.orm import Session
import asyncio
from collections import defaultdict
import traceback
from ..db.models.user import User, Group
from ..db.models.chat import Room, Message as DBMessage, RoomMember
from ..schemas.message import MessageDetail, FriendInfo, GroupInfo
from ..db.session import get_db

class MessageService:
    # 添加消息队列
    _pending_messages = defaultdict(list)# 离线消息队列
    _retry_lock = asyncio.Lock()# 重试锁
    _retry_task = None# 重试任务
    
    @staticmethod
    def parse_last_id(lastId: Optional[str]) -> Optional[int]:
        """安全解析lastId参数"""
        if not lastId or not lastId.strip():
            return None
        try:
            return int(lastId)
        except ValueError:
            return None
    
    @staticmethod
    def verify_room_access(db: Session, room_id: int, user_id: int) -> Tuple[Room, RoomMember]:
        """验证用户对房间的访问权限，返回房间和成员信息"""
        from fastapi import HTTPException
        
        # 验证房间存在
        room = db.query(Room).filter(Room.id == room_id).first()
        if not room:
            raise HTTPException(status_code=404, detail={"message": "会话房间不存在"})
        
        # 验证用户是否有权限访问该房间
        room_member = db.query(RoomMember).filter(
            RoomMember.room_id == room_id,
            RoomMember.user_id == user_id
        ).first()
        
        if not room_member:
            raise HTTPException(status_code=403, detail={"message": "无权访问此会话"})
            
        return room, room_member
    
    @staticmethod
    def get_room_context(db: Session, room: Room, current_user_id: int) -> Tuple[Optional[Dict], Optional[Dict]]:
        """获取房间上下文（好友或群组信息）"""
        friend = None
        group = None
        
        room_type = getattr(room, 'type', 0)  # 安全获取type属性，默认为0
        
        if room_type == 0:  # 私聊
            # 获取对方用户
            other_member = db.query(RoomMember).filter(
                RoomMember.room_id == room.id,
                RoomMember.user_id != current_user_id
            ).first()
            
            if other_member:
                target_user = db.query(User).filter(User.id == other_member.user_id).first()
                
                if target_user:
                    # 直接创建字典而不是FriendInfo对象
                    friend = {
                        "id": target_user.id,
                        "remark": None,
                        "userId": target_user.id,
                        "nickname": target_user.nickname,
                        "avatar": target_user.avatar,
                        "sex": target_user.sex,
                        "online": 1 if target_user.online else 0,
                        "email": target_user.email,
                        "last_at": target_user.last_at,
                        "created_at": target_user.created_at
                    }
        else:  # 群聊
            
            # 由于已删除group_id字段，我们直接使用room本身的信息
            group = {
                "id": room.id,
                "name": room.name,
                "avatar": room.avatar
            }
                
        return friend, group
    
    @staticmethod
    def format_message(msg: DBMessage, sender: User, include_datetime: bool = True) -> Dict[str, Any]:
        """格式化消息为前端所需格式"""
        # 确保类型是整数
        msg_type = int(msg.type) if isinstance(msg.type, str) else msg.type
        # 确保状态是整数
        status = int(msg.status) if isinstance(msg.status, str) else msg.status
        
        result = {
            "id": int(msg.id),  # 确保ID是整数
            "type": msg_type,
            "text": msg.content if msg_type == 0 else None,
            "image": msg.content if msg_type == 1 else None,
            "audio": msg.content if msg_type == 2 else None,
            "file": msg.content if msg_type == 3 else None,
            "status": status,
            "sendStatus": 2,  # 添加sendStatus字段，2表示发送成功
            "createdAt": msg.created_at.strftime("%Y-%m-%d %H:%M:%S"),
            "userId": int(sender.id),  # 确保用户ID是整数
            "userBusinessId": sender.userid,  # 添加业务ID
            "nickname": sender.nickname,
            "avatar": sender.avatar,
            "sex": int(sender.sex) if sender.sex is not None else 0,  # 确保性别是整数
            "email": sender.email,
            # 添加online字段，确保是整数
            "online": 1 if sender.online else 0
        }
        
        # 仅在内部使用时添加原始datetime对象
        if include_datetime:
            result["created_at"] = msg.created_at
        
        return result
    
    @staticmethod
    def get_messages(db: Session, room_id: int, last_id: Optional[int] = None, size: int = 10) -> List[Dict[str, Any]]:
        """获取消息列表"""
        # 构建查询
        query = db.query(DBMessage).filter(DBMessage.room_id == room_id)
        if last_id:
            query = query.filter(DBMessage.id < last_id)
        
        # 执行查询
        messages = query.order_by(DBMessage.id.desc()).limit(size).all()
        
        # 构建响应数据
        response_list = []
        for msg in messages:
            # 获取消息发送者信息
            sender = db.query(User).filter(User.id == msg.sender_id).first()
            if not sender:
                continue
                
            # 格式化消息
            message_detail = MessageService.format_message(msg, sender)
            response_list.append(message_detail)
            
        return response_list
    
    @staticmethod
    def create_message(db: Session, room_id: int, sender_id: int, msg_type: int, content: str) -> DBMessage:
        """创建新消息"""
        new_message = DBMessage(
            room_id=room_id,
            sender_id=sender_id,
            type=str(msg_type),
            content=content,
            status=1,
            created_at=datetime.now()
        )
        db.add(new_message)
        db.commit()
        db.refresh(new_message)
        return new_message
    
    @staticmethod
    async def retry_pending_messages(ws_handler, user_id: int):
        """重试发送离线消息"""
        async with MessageService._retry_lock:
            if user_id in MessageService._pending_messages:
                pending = MessageService._pending_messages[user_id]
                success_count = 0
                failed_count = 0
                
                # 创建消息副本进行迭代，避免迭代过程中修改列表
                messages_to_process = pending.copy()
                
                # 先按消息ID去重
                unique_messages = {}
                for msg in messages_to_process:
                    msg_id = msg.get("_message_id", None)
                    if msg_id:
                        # 如果消息ID已存在，只保留最新的一条
                        if msg_id not in unique_messages or msg.get("_retry_count", 0) > unique_messages[msg_id].get("_retry_count", 0):
                            unique_messages[msg_id] = msg
                    else:
                        # 没有消息ID的消息仍然尝试发送
                        unique_messages[f"no_id_{id(msg)}"] = msg
                
                # 处理去重后的消息
                for msg in unique_messages.values():
                    try:
                        # 限制最大重试次数
                        retry_count = msg.get("_retry_count", 0)
                        if retry_count >= 5:  # 最多重试5次
                            print(f"消息重试次数超限，放弃发送: {msg.get('_message_id', 'unknown')}")
                            pending.remove(msg)
                            failed_count += 1
                            continue
                            
                        # 更新重试次数
                        msg["_retry_count"] = retry_count + 1
                        
                        # 确保消息格式正确
                        if msg.get("type") in [1, 2] and "body" in msg:  # 私聊或群聊消息
                            # 确保body中的id字段是roomId
                            if "roomId" in msg["body"]:
                                if "id" not in msg["body"] or msg["body"]["id"] != msg["body"]["roomId"]:
                                    msg["body"]["id"] = msg["body"]["roomId"]
                                    print(f"离线消息格式修正: 设置id字段为roomId: {msg['body']['roomId']}")
                        
                        # 尝试发送消息
                        success = await ws_handler.send_message(user_id, msg)
                        if success:
                            # 发送成功后从队列中移除
                            if msg in pending:
                                pending.remove(msg)
                            success_count += 1
                        else:
                            failed_count += 1
                    except Exception as e:
                        print(f"重试发送消息失败: {str(e)}")
                        failed_count += 1
                        
                # 如果所有消息都处理完毕，清空队列
                if not pending:
                    del MessageService._pending_messages[user_id]
                    
                print(f"为用户 {user_id} 重试发送离线消息: 成功 {success_count} 条, 失败 {failed_count} 条")
    
    @staticmethod
    async def notify_message(ws_handler, target_user_id: int, room_id: int, message_data: Dict[str, Any], chat_info: Dict[str, Any] = None, is_group: bool = False, is_sender: bool = False):
        """发送消息通知"""
        try:
            # 序列化消息数据
            message_data = MessageService.json_serializable(message_data)
            if chat_info:
                chat_info = MessageService.json_serializable(chat_info)
            
            # 构建消息体
            message_body = {
                "type": 2 if is_group else 1,  # 1:私聊 2:群聊
                "body": {
                    "id": room_id,
                    "roomId": room_id,
                    "userId": target_user_id if not is_sender else message_data.get("userId"),
                    "message": message_data
                }
            }
            
            # 添加好友或群组信息
            if chat_info:
                if is_group:
                    message_body["body"]["group"] = chat_info
                else:
                    message_body["body"]["friend"] = chat_info
            
            # 发送消息
            if target_user_id in ws_handler.active_connections:
                success = await ws_handler.send_message(target_user_id, message_body)
                if not success:
                    # 如果发送失败，加入重试队列
                    message_body["_message_id"] = message_data.get("id")  # 添加消息ID用于去重
                    MessageService._pending_messages[target_user_id].append(message_body)
                    return False
                return True
            else:
                # 用户离线，加入重试队列
                message_body["_message_id"] = message_data.get("id")  # 添加消息ID用于去重
                MessageService._pending_messages[target_user_id].append(message_body)
                return False
            
        except Exception as e:
            print(f"发送消息通知失败: {str(e)}")
            print(traceback.format_exc())
            return False
    
    @staticmethod
    def json_serializable(obj):
        """将对象转换为JSON可序列化的格式"""
        if isinstance(obj, datetime):
            return obj.strftime("%Y-%m-%d %H:%M:%S")
        elif isinstance(obj, dict):
            return {k: MessageService.json_serializable(v) for k, v in obj.items()}
        elif isinstance(obj, list):
            return [MessageService.json_serializable(item) for item in obj]
        elif hasattr(obj, '__dict__'):  # 处理自定义对象
            return MessageService.json_serializable(obj.__dict__)
        return obj
    
    @staticmethod
    async def setup_retry_task():
        """设置定期重试任务"""
        if MessageService._retry_task is None:
            MessageService._retry_task = asyncio.create_task(MessageService._periodic_retry())
            print("消息重试任务已启动")
    
    @staticmethod
    async def _periodic_retry():
        """定期检查并重试发送队列中的消息"""
        from app.api.ws import ws_handler
        
        try:
            while True:
                try:
                    # 获取所有有待发送消息的用户
                    user_ids = list(MessageService._pending_messages.keys())
                    
                    if user_ids:
                        print(f"定期重试: 发现 {len(user_ids)} 个用户有待发送消息")
                        
                        for user_id in user_ids:
                            # 检查用户是否在线
                            if user_id in ws_handler.active_connections and ws_handler.connection_status.get(user_id, False):
                                try:
                                    await MessageService.retry_pending_messages(ws_handler, user_id)
                                except Exception as e:
                                    print(f"重试用户 {user_id} 的消息失败: {str(e)}")
                                    
                except Exception as e:
                    print(f"定期重试任务错误: {str(e)}")
                    print(traceback.format_exc())
                
                # 每60秒检查一次
                await asyncio.sleep(60)
                
        except asyncio.CancelledError:
            print("消息重试任务已取消")
        except Exception as e:
            print(f"消息重试循环错误: {str(e)}")
            print(traceback.format_exc())
            # 出错后重启任务
            asyncio.create_task(MessageService._periodic_retry())